package com.gewu.pm.utils;

import java.math.BigDecimal;
import java.math.RoundingMode;

/**
 * 数学工具类 - 精确的加减乘除运算
 * 
 * 功能：
 * - 解决浮点数精度问题
 * - 提供金额专用计算方法
 * - 确保金融级别的精度
 * 
 * 使用场景：
 * - 金额计算（订单、支付、统计）
 * - 需要精确小数的场景
 * - 避免 0.1 + 0.2 = 0.30000000000000004 这类问题
 */
public final class MathUtils {
    
    /** 默认除法精度（小数点后10位） */
    private static final int DEF_DIV_SCALE = 10;
    
    /** 金额默认精度（小数点后2位） */
    private static final int MONEY_SCALE = 2;

    private MathUtils() {
        // 工具类不允许实例化
    }

    // ========== 基础运算（通用） ==========

    /**
     * 精确加法
     * 
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */
    public static double add(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    /**
     * 精确减法
     * 
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */
    public static double sub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 精确乘法
     * 
     * @param v1 被乘数
     * @param v2 乘数
     * @return 两个参数的积
     */
    public static double mul(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 精确除法（默认精度10位）
     * 
     * @param v1 被除数
     * @param v2 除数
     * @return 两个参数的商
     * @throws ArithmeticException 除数为0时抛出
     */
    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    /**
     * 精确除法（指定精度）
     * 
     * @param v1 被除数
     * @param v2 除数
     * @param scale 小数点后保留位数
     * @return 两个参数的商
     * @throws IllegalArgumentException scale < 0 时抛出
     * @throws ArithmeticException 除数为0时抛出
     */
    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("精度必须是正整数或零");
        }
        if (v2 == 0) {
            throw new ArithmeticException("除数不能为0");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 四舍五入（指定精度）
     * 
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     * @throws IllegalArgumentException scale < 0 时抛出
     */
    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("精度必须是正整数或零");
        }
        BigDecimal b = new BigDecimal(Double.toString(v));
        return b.setScale(scale, RoundingMode.HALF_UP).doubleValue();
    }

    // ========== 金额专用方法（推荐用于订单、支付等场景） ==========

    /**
     * 金额加法（自动保留2位小数）
     * 
     * 使用场景：累加订单金额、统计总额
     * 
     * @param v1 金额1
     * @param v2 金额2
     * @return 两个金额的和（保留2位小数）
     */
    public static double moneyAdd(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.add(b2).setScale(MONEY_SCALE, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 金额减法（自动保留2位小数）
     * 
     * 使用场景：计算找零、优惠后金额
     * 
     * @param v1 被减数
     * @param v2 减数
     * @return 两个金额的差（保留2位小数）
     */
    public static double moneySub(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.subtract(b2).setScale(MONEY_SCALE, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 金额乘法（自动保留2位小数）
     * 
     * 使用场景：价格 × 数量、折扣计算
     * 
     * @param price 价格
     * @param quantity 数量
     * @return 小计金额（保留2位小数）
     */
    public static double moneyMul(double price, double quantity) {
        BigDecimal b1 = new BigDecimal(Double.toString(price));
        BigDecimal b2 = new BigDecimal(Double.toString(quantity));
        return b1.multiply(b2).setScale(MONEY_SCALE, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 金额除法（自动保留2位小数）
     * 
     * 使用场景：计算单价、平均金额
     * 
     * @param v1 被除数
     * @param v2 除数
     * @return 商（保留2位小数）
     * @throws ArithmeticException 除数为0时抛出
     */
    public static double moneyDiv(double v1, double v2) {
        if (v2 == 0) {
            throw new ArithmeticException("除数不能为0");
        }
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.divide(b2, MONEY_SCALE, RoundingMode.HALF_UP).doubleValue();
    }

    /**
     * 金额四舍五入（保留2位小数）
     * 
     * 使用场景：格式化显示金额
     * 
     * @param amount 金额
     * @return 四舍五入后的金额（保留2位小数）
     */
    public static double moneyRound(double amount) {
        return round(amount, MONEY_SCALE);
    }

    /**
     * 比较两个数的大小
     * 
     * @param v1 数值1
     * @param v2 数值2
     * @return v1 > v2 返回1，v1 = v2 返回0，v1 < v2 返回-1
     */
    public static int compare(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1));
        BigDecimal b2 = new BigDecimal(Double.toString(v2));
        return b1.compareTo(b2);
    }

    /**
     * 判断两个金额是否相等（精确到2位小数）
     * 
     * @param v1 金额1
     * @param v2 金额2
     * @return true-相等，false-不相等
     */
    public static boolean moneyEquals(double v1, double v2) {
        BigDecimal b1 = new BigDecimal(Double.toString(v1)).setScale(MONEY_SCALE, RoundingMode.HALF_UP);
        BigDecimal b2 = new BigDecimal(Double.toString(v2)).setScale(MONEY_SCALE, RoundingMode.HALF_UP);
        return b1.compareTo(b2) == 0;
    }

    // ========== 兼容旧方法 ==========

    /**
     * 加法（保留2位小数）
     * @deprecated 建议使用 {@link #moneyAdd(double, double)}
     */
    @Deprecated
    public static double add2(double v1, double v2) {
        return moneyAdd(v1, v2);
    }

    /**
     * 保留两位小数
     * @deprecated 建议使用 {@link #moneyRound(double)} 或 {@link #round(double, int)}
     */
    @Deprecated
    public static double roundTwoDecimals(double d) {
        return round(d, MONEY_SCALE);
    }
}
